package othello.players.benchmarkplayers;

import java.awt.Color;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Random;
import java.util.Iterator;

import othello.model.IBoard;
import othello.players.IPlayerInfo;
import othello.players.AbstractPlayer;

/**
 * Implements mobility strategy from Van Eck paper
 */
public class Mobility extends AbstractPlayer {
    
    protected Color _hisColor = null;

    private Random _rand;

    public Mobility() {
        super("Mobility");
    }

    @Override
    public void joinBoard(IBoard board, Color color) {
        super.joinBoard(board, color);
        setRand(new Random());
    }
    
    private int positionOccupance(IBoard brd, int r, int c) {
        Color color = brd.playerAt(r,c);
        if (color == null){
            return 0;
        }
        if (color.equals(this.getColor())) {
            return 1;
        }
        
        return -1;
    }
    
    private double percentOccupied(IBoard brd){
        double count = 0.0;
        for (int r = 0; r < brd.getRowCount(); r++) {
            for (int c = 0; c < brd.getColCount(); c++) {
                count += positionOccupance(brd,r,c)*positionOccupance(brd,r,c);
            }
        }
        return 100.0*count/64.0;
    }
    
    boolean endGame(IBoard brd) {
        int cornersCovered  =   positionOccupance(brd,0,0)*positionOccupance(brd,0,0) +
                                positionOccupance(brd,0,7)*positionOccupance(brd,0,7) +
                                positionOccupance(brd,7,0)*positionOccupance(brd,7,0) +
                                positionOccupance(brd,7,7)*positionOccupance(brd,7,7);                                
        return (cornersCovered == 4) || (percentOccupied(brd) >= 80.0) ;
    }
    
    @Override
	public void endGame() {
	}

    @Override
    public void beginTurn() {
        if (_hisColor == null) {
            Iterator<IPlayerInfo> pInfoItr = board.getPlayers();
            _hisColor = (pInfoItr.next()).getColor();
            if (_hisColor == color) {
                _hisColor = (pInfoItr.next()).getColor();
            }
        }
        
        boolean inEndGame = endGame(board);
        
        ArrayList<Point> moves = validMoves();
        
        Point bestMove = new Point(0,0);
                
        if (moves.isEmpty()) {
            getBoard().pass(this);
        } else {
            bestMove = moves.get(0);
            double maxEval = Double.MIN_VALUE;
            for(Iterator<Point> mv = moves.iterator(); mv.hasNext();){
                Point tempPoint = mv.next();
                IBoard hypoBrd = board.hypotheticallyPlacePiece(this.getColor(), tempPoint.y, tempPoint.x);
                double eval = 0;
                
                if (inEndGame){
                        eval = evalEndgame(hypoBrd);
                } else {
                        eval = evalFunc(hypoBrd);                        
                }
                if (eval > maxEval){
                    bestMove = new Point(tempPoint);
                    maxEval = eval;
                }
            }
        }
        
        board.placePiece(this,bestMove.y,bestMove.x);
    }

    @Override
    public void endTurn() {
    }

    /**
     * Finds all the valid moves for the given player on the given board.
     *
     * @param board
     * @param playerColor
     * @return
     */

    protected double numberOfCorners(IBoard board, Color playerColor){
		double sum = 0.0;
		int[] corners = {0,7};
		
	    for (int i = 0; i < corners.length; i++){
		  for (int j = 0; j < corners.length; j++){
	        if (playerColor.equals(board.playerAt(corners[i],corners[j]))){
		      sum++;
		    }
		  }
	    }
	    return sum;
    }
    
    protected double evalFunc(IBoard hyp){
        double w1 = 10.0;
        double w2 = 1.0;
        return w1*(numberOfCorners(hyp,this.getColor()) - numberOfCorners(hyp,_hisColor)) +         w2*(numberOfValidMoves(hyp,this.getColor())-numberOfValidMoves(hyp,_hisColor))/(numberOfValidMoves(hyp,this.getColor())+numberOfValidMoves(hyp,_hisColor));        
    }
    
    public int evalEndgame(IBoard brd) {
        int eval = 0;
        for (int r = 0; r < brd.getRowCount(); r++) {
            for (int c = 0; c < brd.getColCount(); c++) {
                eval += positionOccupance(brd,r,c);
            }
        }
        return eval;
    }
    
    
    protected double numberOfValidMoves(IBoard board, Color playerColor) {
        double mvs = 0.0;        
        for (int r = 0; r < board.getRowCount(); r++) {
            for (int c = 0; c < board.getColCount(); c++) {
                if (board.isValidMove(playerColor, r, c)) {
                    mvs++;
                }
            }
        }
        return mvs;
    }
    
    /**
     * Finds all the valid moves for the given player on the given board.
     *
     * @param board
     * @param playerColor
     * @return
     */
    
    protected ArrayList<Point> validMoves(IBoard board, Color playerColor) {
        ArrayList<Point> mvs = new ArrayList<Point>();

        // Find all valid moves.
        for (int r = 0; r < board.getRowCount(); r++) {
            for (int c = 0; c < board.getColCount(); c++) {
                if (board.isValidMove(playerColor, r, c)) {
                    // Make the first valid move we can find.
                    mvs.add(new Point(c, r));
                }
            }
        }
       
        return mvs;
    }

    /**
     * Finds all the valid moves for this player on its current board.
     *
     * @return
     */
    protected ArrayList<Point> validMoves() {
        return validMoves(getBoard(), getColor());
    }

    /**
     * @return the board
     */
    public IBoard getBoard() {
        return board;
    }

    /**
     * @param board the board to set
     */
    public void setBoard(IBoard board) {
        this.board = board;
    }

    /**
     * @return the _rand
     */
    public Random getRand() {
        return _rand;
    }

    /**
     * @param rand the _rand to set
     */
    public void setRand(Random rand) {
        this._rand = rand;
    }
}
